#pragma once

#include "evaluationkeys.h"

namespace Microsoft
{
    namespace Research
    {
        namespace SEAL
        {
            ref class BigPoly;

            ref class KeyGenerator;

            /**
            <summary>Represents an array of evaluation keys (each represented by a BigPoly) to use during Evaluator
            operations.</summary>

            <remarks>
            <para>
            Represents an array of evaluation keys (each represented by a <see cref="BigPoly"/>) to use during Evaluator
            operations. The evaluation keys are typically initially created by the <see cref="KeyGenerator"/> for a matching
            secret key. The evaluation keys may also be saved and loaded from a stream with the <see cref="Save()"/> and
            <see cref="Load()"/> functions.
            </para>
            <para>
            While in most cases a user will not need to manipulate the evaluation keys directly, the class provides all of the
            functionality of a BigPoly array. The size of the array (which can be read with <see cref="Count"/>) is set
            initially by the constructor and can be resized either with the <see cref="Resize()"/> function or with the 
            <see cref="Set()"/> function. The index functions allow reading/writing individual BigPoly's in the array.
            </para>
            <para>
            In general, reading from an EvaluationKeys instance is thread-safe while mutating is not. That is, once a set of
            EvaluationKeys is generated by the KeyGenerator, the same EvaluationKeys instance can be used by several concurrent
            Evaluator instances as each instance only reads from the EvaluationKeys. For more specialized cases where a user may
            concurrently read and write from an EvaluationKeys instance, care must be taken to prevent race conditions.
            Specifically, the underlying BigPoly array may be freed whenever a resize occurs, the EvaluationKeys instance is
            destroyed, or an assignment operation occurs, which will invalidate the BigPoly references returned by index
            function. When it is known that a resize will not occur, concurrent reading and mutating may still fail when
            reading/writing the same BigPoly due to the BigPoly's lack of thread safety.
            </para>
            </remarks>
            <seealso cref="BigPoly">See BigPoly for more information about the individual BigPoly's contained in the
            EvaluationKeys.</seealso>
            */
            public ref class EvaluationKeys
            {
            public:
                /**
                <summary>Creates an empty EvaluationKeys instance with a size of zero.</summary>

                <remarks>
                Creates an empty EvaluationKeys instance with a size of zero. No memory is allocated by this constructor.
                </remarks>
                */
                EvaluationKeys();

                /**
                <summary>Creates a zero-initialized EvaluationKeys instance with the specified size.</summary>
                <param name="count">The number of BigPoly's to allocate space for</param>
                <exception cref="System::ArgumentException">if count is negative</exception>
                */
                EvaluationKeys(int count);

                /**
                <summary>Creates a deep copy of an EvaluationKeys instance.</summary>

                <remarks>
                Creates a deep copy of an EvaluationKeys instance. The created EvaluationKeys instance will have the same count and
                BigPoly values as the original.
                </remarks>
                <param name="copy">The EvaluationKeys instance to copy from</param>
                <exception cref="System::ArgumentNullException">If copy is null</exception>
                */
                EvaluationKeys(EvaluationKeys ^copy);

                /**
                <summary>Returns the number of BigPoly's contained in the EvaluationKeys instance.</summary>
                */
                property int Count {
                    int get();
                }

                /**
                <summary>Returns a reference to the BigPoly at the specified index.</summary>

                <remarks>
                <para>
                Returns a reference to the BigPoly at the specified index.
                </para>
                <para>
                The returned BigPoly is a reference backed by the EvaluationKeys internal array. As such, it is only valid until the
                EvaluationKeys instance is resized or destroyed.
                </para>
                </remarks>
                <exception cref="System::ArgumentOutOfRangeException">If polyIndex is not within [0, <see cref="Count"/>)</exception>
                */
                property BigPoly ^default[int]{
                    BigPoly ^get(int poly_index);
                }

                /**
                <summary>Resizes the EvaluationKeys internal array to store the specified number of BigPoly's, copying over the old
                BigPoly as much as will fit.</summary>
                <param name="count">The number of BigPoly's to allocate space for</param>
                <exception cref="System::ArgumentException">if count is negative</exception>
                */
                void Resize(int count);

                /**
                <summary>Resets the EvaluationKeys instance to an empty, zero-sized instance.</summary>

                <remarks>
                Resets the EvaluationKeys instance to an empty, zero-sized instance. Any space allocated by the EvaluationKeys instance
                is deallocated.
                </remarks>
                */
                void Clear();

                /**
                <summary>Overwrites the EvaluationKeys instance with the BigPolys in the specified EvaluationKeys instance.</summary>

                <remarks>
                Overwrites the EvaluationKeys instance with the BigPolys in the specified EvaluationKeys instance. After assignment,
                the size of EvaluationKeys matches the size of the assigned EvaluationKeys instance.
                </remarks>
                <param name="assign">The EvaluationKeys instance to whose value should be assigned to the current EvaluationKeys
                instance</param>
                <exception cref="System::ArgumentNullException">If assign is null</exception>
                */
                void Set(EvaluationKeys ^assign);

                /**
                <summary>Saves the EvaluationKeys instance to an output stream.</summary>

                <remarks>
                Saves the EvaluationKeys instance to an output stream. The output is in binary format and not human-readable. The
                output stream must have the "binary" flag set.
                </remarks>
                <param name="stream">The stream to save the EvaluationKeys to</param>
                <exception cref="System::ArgumentNullException">If stream is null</exception>
                <seealso cref="Load()">See Load() to load a saved EvaluationKeys instance.</seealso>
                */
                void Save(System::IO::Stream ^stream);

                /**
                <summary>Loads an EvaluationKeys instance from an input stream overwriting the current EvaluationKeys
                instance.</summary>
                <param name="stream">The stream to load the EvaluationKeys instance from</param>
                <exception cref="System::ArgumentNullException">If stream is null</exception>
                <seealso cref="Save()">See Save() to save an EvaluationKeys instance.</seealso>
                */
                void Load(System::IO::Stream ^stream);

                /**
                <summary>Destroys the EvaluationKeys, including deallocating any internally allocated space.</summary>
                */
                ~EvaluationKeys();

                /**
                <summary>Destroys the EvaluationKeys, including deallocating any internally allocated space.</summary>
                */
                !EvaluationKeys();

                /**
                <summary>Returns a reference to the underlying C++ EvaluationKeys.</summary>
                */
                seal::EvaluationKeys &GetKeys();

            internal:
                /**
                <summary>Creates a deep copy of a C++ EvaluationKeys instance.</summary>

                <remarks>
                Creates a deep copy of a C++ EvaluationKeys instance. The created EvaluationKeys instance will have the same count and
                BigPoly values as the original.
                </remarks>
                <param name="value">The EvaluationKeys instance to copy from</param>
                <exception cref="System::ArgumentNullException">If copy is null</exception>
                */
                EvaluationKeys(const seal::EvaluationKeys &value);

                /**
                <summary>Initializes the EvaluationKeys to use the specified C++ EvaluationKeys instance.</summary>
                <remarks>
                Initializes the BigPoly to use the specified C++ EvaluationKeys instance. This constructor does not copy the C++
                EvaluationKeys instance but actually uses the specified C++ EvaluationKeys instance as the backing data. Upon
                destruction, the managed EvaluationKeys will not destroy the C++ EvaluationKeys.
                </remarks>
                <param name="value">The C++ EvaluationKeys to use as the backing EvaluationKeys instance</param>
                */
                EvaluationKeys(seal::EvaluationKeys *value);

            private:
                seal::EvaluationKeys *keys_;

                bool owned_;
            };
        }
    }
}
